#pragma once

#include <nanobdd/common.h>
#include <nanobdd/node.h>
#include <vector>

namespace nanobdd {

class Bucket {
 public:
  Bucket(size_t size) : size_(size), nodes_(size), index_(size), currSize_(0) {}

  Node& operator()(int level, Node* low, Node* high) {

    // search in nodes
    for (int i = 0; i < currSize_; ++i) {
      auto& node = nodes_.at(i);
      if (node.level() == level && node.low() == low && node.high() == high) {
        return node;
      }
    }

    // create the node
    currSize_++;
    auto& node = nodes_.at(currSize_);
    node.level_ = level;
    node.low_ = low;
    node.high_ = high; 

    return node;
  };

 private:
  size_t size_;
  size_t currSize_; // current occupied number of nodes
  std::vector<Node> nodes_;
  std::vector<int> index_;
};

class NodeTable {
 public:
  NodeTable(size_t tableSize, int bucketSize) : tableSize_(tableSize) {
    for (int i = 0; i < tableSize; ++i) {
      buckets_.push_back(std::move(Bucket(bucketSize)));
    }
  };

  /**
   * Get or create a node
  */
  Node& operator()(int level, Node* l, Node* r) {
    auto hash = TRIPLEp(level, l, r) % tableSize_;
    auto& bucket = buckets_.at(hash);

    return bucket(level, l, r);
  };

 private:
  size_t tableSize_;
  std::vector<Bucket> buckets_;
};

} // namespace nanobdd